<?php

/**********************************************************************
**
** A class to download files
** Version 1.0
** Features : 
**      - hide the real path to the file
**      - allow / disallow download resuming
**      - partial download (useful for download managers)
**      - rename the file on the fly
**      - limit download speed
**
** Author: Mourad Boufarguine / EPT <mourad.boufarguine@gmail.com>
**
** License: Public Domain
** Warranty: None
**
***********************************************************************/

class Mediad2_Utility_Bfdownload
{
    
    // just one array to gather all the properties of a download
    private $properties = array("path" => "",       // the real path to the file 
                                "name" => "",       // to rename the file on the fly
                                "extension" => "",  // extension of the file
                                "type" => "",       // the type of the file
                                "size" => "",       // the file size
                                "resume" => "",     // allow / disallow resuming
                                "max_speed" => ""   // speed limit (ko) ( 0 = no limit)
                                );          

    // the constructor
    public function __construct($path, $name="", $resume="off", $max_speed=0){  // by default, resuming is NOT allowed and there is no speed limit
        $name = ($name == "") ? array_pop( explode( '/', strtr( $path, '\\', '/' ) ) ) : $name; // if "name" is not specified, th file won't be renamed
        $file_extension = strtolower(substr(strrchr($path,"."),1));       // the file extension
        switch( $file_extension ) {                                       // the file type
            case "mp3": $content_type="audio/mpeg"; break;
            case "mpg": $content_type="video/mpeg"; break;
            case "avi": $content_type="video/x-msvideo"; break;
            case "wmv": $content_type="video/x-ms-wmv";break;
            case "wma": $content_type="audio/x-ms-wma";break;
            case "zip": $content_type="application/zip";break;
            default: $content_type="application/octet-stream";
        }
        $file_size = filesize($path);                                     // the file size
        $this->properties =  array(
                                    "path" => $path, 
                                    "name" => $name, 
                                    "extension" =>$file_extension,
                                    "type"=>$content_type, 
                                    "size" => $file_size, 
                                    "resume" => $resume, 
                                    "max_speed" => $max_speed
                                    );
    }
    
    // public function to get the value of a property
    public function get_property ($property){
        if ( array_key_exists($property,$this->properties) )   // check if the property do exist
            return $this->properties[$property];               // get its value
        else
            return null;                                       // else return null
    }
    
    // public function to set the value of a property        
    public function set_property ($property, $value){
        if ( array_key_exists($property, $this->properties) ){ // check if the property do exist
            $this->properties[$property] = $value;             // set the new value
            return true;
        } else
            return false;
    }
    
    // public function to start the download
    public function download_file($isdelOriFile = false)
    {
        if ( $this->properties['path'] == "" )                 // if the path is unset, then error !
            echo "Nothing to download!";
        else {
            // if resuming is allowed ...
            if ($this->properties["resume"] == "on") {
                if(isset($_SERVER['HTTP_RANGE'])) {            // check if http_range is sent by browser (or download manager)
                    list($a, $range)=explode("=",$_SERVER['HTTP_RANGE']);  
                    ereg("([0-9]+)-([0-9]*)/?([0-9]*)",$range,$range_parts); // parsing Range header
                    $byte_from = $range_parts [1];     // the download range : from $byte_from ...
                    $byte_to = $range_parts [2];       // ... to $byte_to 
                } else
                    if(isset($_ENV['HTTP_RANGE'])) {       // some web servers do use the $_ENV['HTTP_RANGE'] instead
                        list($a, $range)=explode("=",$_ENV['HTTP_RANGE']);
                        ereg("([0-9]+)-([0-9]*)/?([0-9]*)",$range,$range_parts); // parsing Range header
                        $byte_from = $range_parts [1];     // the download range : from $byte_from ...
                        $byte_to = $range_parts [2];       // ... to $byte_to 
                    }else{
                        $byte_from = 0;                         // if no range header is found, download the whole file from byte 0 ...
//                         $byte_to = $this->properties["size"] - 1;   // ... to the last byte
                        $byte_to = $this->properties["size"];
                    }
                if ($byte_to == "") {                            // if the end byte is not specified, ...
//                     $byte_to = $this->properties["size"] -1;    // ... set it to the last byte of the file
                	$byte_to = $this->properties["size"];
                }
                header("HTTP/1.1 206 Patial Content");          // send the partial content header
            // ... else, download the whole file
			} else {
                $byte_from = 0;
//                 $byte_to = $this->properties["size"] - 1;
                $byte_to = $this->properties["size"];
            }
            
            $download_range = $byte_from."-".$byte_to."/".$this->properties["size"]; // the download range
            $download_size = $byte_to - $byte_from;                                  // the download length
            
            // download speed limitation
            if (($speed = $this->properties["max_speed"]) > 0)                       // determine the max speed allowed ...
                $sleep_time = (8 / $speed) * 1e6;                                    // ... if "max_speed" = 0 then no limit (default)
            else
                $sleep_time = 0;
            
            // send the headers    
            header("Pragma: public");                                                // purge the browser cache
            header("Expires: 0");                                                    // ...
            header("Cache-Control:");                                                // ...
            header("Cache-Control: public");                                         // ... 
            header("Content-Description: File Transfer");                            //  
            header("Content-Type: ".$this->properties["type"]);                     // file type
            header('Content-Disposition: attachment; filename="'.$this->properties["name"].'";');
            header("Content-Transfer-Encoding: binary");                             // transfer method
            header("Content-Range: $download_range");                                // download range
            header("Content-Length: $download_size");                                // download length
            
            // send the file content        
            $fp=fopen($this->properties["path"],"rb");      // open the file 
            if(!fp) exit; // if $fp is not a valid stream resource, exit
            fseek($fp,$byte_from);                          // seek to start of missing part   
            while(!feof($fp)){                              // start buffered download  
                set_time_limit(0);                          // reset time limit for big files (has no effect if php is executed in safe mode)
                $buffer = fread($fp, 1024*8);				// send 8ko
                echo $buffer;                   			
                ob_flush();
                flush();
                usleep($sleep_time);                        // sleep (for speed limitation)
            }
            fclose($fp);                                    // close the file
            if($isdelOriFile) {
            	@unlink($this->properties["path"]);
            }
            exit;  
        }
    }
}
